package com.sunny.springboot2.core.handler;

import com.sunny.springboot2.core.exception.CustomException;
import com.sunny.springboot2.core.exception.ErrorResponseEntity;
import com.sunny.springboot2.enums.ExceptionEnums;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * @author sunny
 * @date 2018/11/27
 */
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    /**
     *  定义要捕获的异常 可以多个 @ExceptionHandler({})
     *
     * @param response response
     * @param e exception
     * @author sunny
     * @date 2018/11/27
     * @return com.sunny.springboot2.core.exception.ErrorResponseEntity
     */
    @ExceptionHandler(value = CustomException.class)
    public ErrorResponseEntity customExceptionHandler(HttpServletResponse response, final Exception e) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        CustomException customException = (CustomException) e;
        return new ErrorResponseEntity(customException.getCode(), customException.getMsg());
    }

    /**
     *  捕获 Exception 异常
     *
     * @param response response
     * @param e exception
     * @author sunny
     * @date 2019/06/07
     * @return com.sunny.springboot2.core.exception.ErrorResponseEntity
     */
    @ExceptionHandler(value = Exception.class)
    public ErrorResponseEntity exceptionHandler(HttpServletResponse response, final Exception e) {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

        ErrorResponseEntity entity;
        if (e instanceof CustomException) {
            entity = customExceptionHandler(response, e);
        } else {
            entity = new ErrorResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
        return entity;
    }

    /**
     *  捕获 RuntimeException 异常
     *
     * @param response response
     * @param e exception
     * @author sunny
     * @date 2018/11/27
     * @return com.sunny.springboot2.core.exception.ErrorResponseEntity
     */
    @ExceptionHandler(value = RuntimeException.class)
    public ErrorResponseEntity runtimeExceptionHandler(HttpServletResponse response, final Exception e) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        RuntimeException runtimeException = (RuntimeException) e;
        return new ErrorResponseEntity(400, runtimeException.getMessage());
    }

    /**
     * 通用的接口映射异常处理方
     *
     * @param ex exception
     * @param body body
     * @param headers headers
     * @param status status
     * @param request request
     * @author sunny
     * @date 2018/11/27
     * @return org.springframework.http.ResponseEntity<java.lang.Object>
     */
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (ex instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
            return new ResponseEntity<>(new ErrorResponseEntity(status.value(), exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status);
        }
        if (ex instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex;
            logger.error("参数转换失败，方法：" + exception.getParameter().getMethod().getName() + "，参数：" + exception.getName()
                    + ",信息：" + exception.getLocalizedMessage());
            return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "参数转换失败"), status);
        }
        if (ex instanceof BindException) {
            BindException bindException = (BindException) ex;
            logger.error("参数校验失败：{}", bindException);
            List<ObjectError> allErrors = bindException.getBindingResult().getAllErrors();
            List<String> errorMessages = allErrors.stream().map(error -> error.getDefaultMessage()).collect(Collectors.toList());
            return new ResponseEntity<>(new ErrorResponseEntity(ExceptionEnums.PARAM_EXCEPTION.getErrorCode(), errorMessages.toString()), status);
        }

        return new ResponseEntity<>(new ErrorResponseEntity(status.value(), "参数转换失败"), status);
    }
}
